#include "http.h"
#include <QtNetwork/QNetworkDiskCache>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QCoreApplication>
#include <QtCore/QBuffer>
#include <QtConcurrent/QtConcurrentRun>

Http::Http(QObject *parent) : QObject(parent)
{
    m_network = new QNetworkAccessManager(this);
    m_reply = Q_NULLPTR;
}

Http::~Http()
{
}

void Http::initialize(QNetworkReply *reply)
{	
    connect(reply, SIGNAL(finished()), this, SLOT(finished()));

	connect(reply, SIGNAL(readyRead()), this, SLOT(readyRead()));

    connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64,qint64)));

    connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(replyError(QNetworkReply::NetworkError)));
}

void Http::remain(const QUrl &url)
{
    if (!m_file.commit())
    {
        emit errorReported("can't save downloaded data: " + m_file.errorString());
        emit progressChanged(m_size, m_size);
        return;
    }

    if (m_aborted) {
        QFile::remove(m_file.fileName());
        emit progressChanged(m_size, m_size);
        return;
    }

    if (url.hasFragment())
    {
        QFile file(m_file.fileName());

        if (!file.open(QIODevice::ReadOnly))
        {
            emit errorReported("can't open file: " + file.errorString());
            emit progressChanged(m_size, m_size);
            return;
        }

        QByteArray data = file.readAll();

        QString md5 = QCryptographicHash::hash(data.constData(), QCryptographicHash::Md5).toHex();

        if (md5 != url.fragment())
        {
            emit errorReported("Download the installation package data checksum is incorrect. In order to secure your computer, please re-download.");

            file.remove();
            emit progressChanged(m_size, m_size);
            return;
        }
    }

    emit progressChanged(m_size, m_size);
}

void Http::finished()
{
    QVariant redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);

    if (m_aborted || (m_reply->error() != QNetworkReply::NoError)){
        QUrl url= m_reply->request().url();

        m_reply->deleteLater();
        m_reply = 0;		

        QtConcurrent::run(this, &Http::remain, url);

		return;
	}		

	if (!redirectionTarget.isNull())	{   
        QUrl url = m_reply->request().url();

        m_reply->deleteLater();

        QNetworkRequest request;
        request.setUrl(url.resolved(redirectionTarget.toUrl()));

        request.setRawHeader("Referer", url.toString(QUrl::RemovePath| QUrl::RemoveQuery | QUrl::RemoveFragment).toLocal8Bit());

        m_reply = m_network->get(request);

        m_file.reset();

        this->initialize(m_reply);

		return;
	} 	

    QUrl url= m_reply->request().url();

    QtConcurrent::run(this, &Http::remain, url);

    m_reply->deleteLater();

    m_reply = 0;
}

void Http::readyRead()
{
    while (!m_reply->atEnd()){
        m_file.write(m_reply->readAll());
	}			
}

void Http::replyError(QNetworkReply::NetworkError)
{
    emit errorReported(m_reply->errorString());
}

void Http::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    if (bytesTotal == -1) {
        bytesTotal = m_size;
    }

    emit progressChanged(bytesReceived, bytesTotal + 1); // comit download file.
}

void Http::cancel()
{
    if (m_reply)
        m_reply->abort();

    m_aborted = true;
}

void Http::download()
{
    m_aborted = false;

    m_file.setFileName(m_fileName);

    m_file.open(QIODevice::WriteOnly | QIODevice::Truncate);

    if (!m_file.isOpen())
    {
        this->errorReported(m_file.errorString());
        return;
    }

    QNetworkRequest request(m_url);

    QSslConfiguration config;

    config.setPeerVerifyMode(QSslSocket::VerifyNone);
    config.setProtocol(QSsl::TlsV1_0);

    request.setSslConfiguration(config);
    request.setUrl(m_url);

    request.setRawHeader("Referer", request.url().toString(QUrl::RemovePath| QUrl::RemoveQuery | QUrl::RemoveFragment).toLocal8Bit());

    m_reply = m_network->get(request);

    this->initialize(m_reply);
}

